<script lang="ts" setup>
import { onMounted, onUnmounted } from 'vue'
import rootBg from '@/assets/chart/root.png'
import gatewayBlue from '@/assets/chart/网关蓝.png'
import deviceBlue from '@/assets/chart/装置蓝.png'
import terminalBlue from '@/assets/chart/终端蓝.png'
import gatewayGrey from '@/assets/chart/网关灰.png'
import deviceGrey from '@/assets/chart/装置灰.png'
import terminalGrey from '@/assets/chart/终端灰.png'
import gatewayYellow from '@/assets/chart/网关黄.png'
import deviceYellow from '@/assets/chart/装置黄.png'
import terminalYellow from '@/assets/chart/终端黄.png'
import textBg from '@/assets/chart/textBg.png'
import G6 from '@antv/g6'
/*
 * 主要是线的区别，如当左侧数据过多时会出现线重叠
 */
onMounted(() => {
    handleOriginalData()
    readyChart()
    initChart()
})
onUnmounted(() => {
    if (graph && !graph.get('destroyed')) {
        graph.destroy()
    }
})
const COLLAPSE_ICON = function COLLAPSE_ICON(x: number, y: number, r: number) {
    return [
        ['M', x - r, y - r],
        ['a', r, r, 0, 1, 0, r * 2, 0],
        ['a', r, r, 0, 1, 0, -r * 2, 0],
        ['M', x + 2 - r, y - r],
        ['L', x + r - 2, y - r],
    ]
}
const EXPAND_ICON = function EXPAND_ICON(x: number, y: number, r: number) {
    return [
        ['M', x - r, y - r],
        ['a', r, r, 0, 1, 0, r * 2, 0],
        ['a', r, r, 0, 1, 0, -r * 2, 0],
        ['M', x + 2 - r, y - r],
        ['L', x + r - 2, y - r],
        ['M', x, y - 2 * r + 2],
        ['L', x, y - 2],
    ]
}
let graph: any = ''
const originalData = {
    id: '1',
    label: '业务系统',
    icon: rootBg,
    children: [
        {
            id: '1-1',
            label: '安全接入网关01',
            icon: gatewayBlue,
            // collapsed: true, // 默认展开
            children: [
                {
                    id: '1-1-1',
                    label: '空气质量检测器1',
                    icon: deviceBlue,
                },
                {
                    id: '1-1-2',
                    label: '空气质量检测器2',
                    icon: deviceYellow,
                    children: [
                        {
                            id: '1-1-2-1',
                            label: '终端设备',
                            icon: terminalGrey,
                            children: [
                                {
                                    id: '1-1-2-1-1',
                                    label: '摄像头',
                                    icon: deviceGrey,
                                    hasCount: true,
                                    count: 18,
                                },
                            ],
                        },
                    ],
                },
            ],
        },
        {
            id: '1-2',
            label: '安全接入网关02',
            icon: gatewayBlue,
            children: [
                {
                    id: '1-2-1',
                    label: '空气质量检测器1',
                    icon: deviceGrey,
                },
                {
                    id: '1-2-2',
                    label: '空气质量检测器2',
                    icon: terminalGrey,
                    children: [
                        {
                            id: '1-2-2-1',
                            label: '道法大声道发泡剂',
                            icon: deviceGrey,
                            hasCount: true,
                            count: 9,
                            children: [
                                {
                                    id: '1-2-2-1-1',
                                    label: '摄像头',
                                    icon: deviceGrey,
                                },
                                {
                                    id: '1-2-2-1-2',
                                    label: '智能插座',
                                    icon: terminalGrey,
                                    children: [
                                        {
                                            id: '1-2-2-1-2-1',
                                            label: 'SN00150096503',
                                            icon: deviceGrey,
                                        },
                                        {
                                            id: '1-2-2-1-2-2',
                                            label: 'SN00150096503',
                                            icon: terminalGrey,
                                        },
                                    ],
                                },
                            ],
                        },
                        {
                            id: '1-2-2-2',
                            label: '智能插座测赛道法',
                            icon: terminalGrey,
                            hasCount: true,
                            count: 22,
                        },
                    ],
                },
                {
                    id: '1-2-3',
                    label: '空气质量检测器1',
                    icon: deviceGrey,
                },
                {
                    id: '1-2-4',
                    label: '空气质量检测器1',
                    icon: deviceGrey,
                },
                {
                    id: '1-2-5',
                    label: '空气质量检测器1',
                    icon: deviceGrey,
                },
            ],
        },
        {
            id: '1-3',
            label: '安全接入网关03',
            icon: gatewayGrey,
        },
        {
            id: '1-4',
            label: '安全接入网关04',
            icon: gatewayYellow,
            children: [
                {
                    id: '1-4-1',
                    label: '数据采集装置01',
                    icon: terminalYellow,
                    children: [
                        {
                            id: '1-4-1-1',
                            label: '智能门锁',
                            icon: terminalYellow,
                            children: [
                                {
                                    id: '1-4-1-1-1',
                                    label: '摄像头',
                                    icon: terminalYellow,
                                },
                                {
                                    id: '1-4-1-1-2',
                                    label: '摄像头',
                                    icon: terminalYellow,
                                },
                                {
                                    id: '1-4-1-1-3',
                                    label: '摄像头',
                                    icon: terminalYellow,
                                },
                            ],
                        },
                        {
                            id: '1-4-1-2',
                            label: '摄像头',
                            icon: terminalYellow,
                        },
                        {
                            id: '1-4-1-3',
                            label: '摄像头',
                            icon: terminalYellow,
                        },
                    ],
                },
            ],
        },
        {
            id: '1-5',
            label: '安全接入网关05',
            icon: gatewayYellow,
            children: [
                {
                    id: '1-5-1',
                    label: '摄像头',
                    icon: terminalBlue,
                },
                {
                    id: '1-5-2',
                    label: '摄像头',
                    icon: terminalYellow,
                },
                {
                    id: '1-5-3',
                    label: '摄像头',
                    icon: terminalYellow,
                },
            ],
        },
        {
            id: '1-6',
            label: '安全接入网关06',
            icon: gatewayYellow,
        },
    ],
}
const defaultStateStyles = {
    hover: {
        stroke: '#1890ff',
        lineWidth: 2,
    },
}
const defaultEdgeStyle = {
    stroke: '#1890ff',
}
const defaultLayout = {
    type: 'compactBox',
    direction: 'TB',
    getId: function getId(d: any) {
        return d.id
    },
    // 节点高度
    getHeight: function getHeight() {
        return 16
    },
    // 节点宽度
    getWidth: function getWidth() {
        return 16
    },
    // 节点纵向间距
    getVGap: function getVGap() {
        return 50
    },
    // 节点横向间距
    getHGap: function getHGap() {
        return 70
    },
}
const defaultLabelCfg = {
    style: {
        fill: '#fff',
        fontSize: 14,
    },
}
const handleOriginalData = () => {
    const handleTree = (tree: any, curIndex: number, brotherCount: number) => {
        tree.curIndex = curIndex
        const childCount: number = tree.children?.length
        const ave = brotherCount % 2
        const intAve = Math.floor(brotherCount / 2)
        if (brotherCount == 1) {
            tree.orderIndex = 0
        } else if (ave == 0) {
            // 偶数
            tree.orderIndex =
                curIndex < intAve ? -(intAve - curIndex) : curIndex - intAve + 1
            tree.hasTwoChild = brotherCount == 2 // 只有两个节点可以特殊处理一下,让其共用一条线
        } else if (ave != 0) {
            // 奇数
            tree.orderIndex =
                curIndex < intAve
                    ? -(intAve - curIndex)
                    : curIndex == intAve
                    ? 0
                    : curIndex - intAve
        }
        if (childCount) {
            tree.childCount = childCount
            tree.children.forEach((item: any, index: number) => {
                handleTree(item, index, childCount)
            })
        }
    }
    handleTree(originalData, 0, 1)
}
const handleTransfer = (str: string) => {
    // 1个中文字长度给到14，其他给到8.5
    const chineseRegex = /[\u4e00-\u9fa5]/g
    const chineseCount = (str.match(chineseRegex) || []).length
    const englishCount = str.length - chineseCount
    const len = 14 * chineseCount + 8.5 * englishCount + 15 // 15为左右的总边距，可理解为padding
    return len
}
const readyChart = () => {
    G6.Util.traverseTree(originalData, (d: any) => {
        d.leftIcon = {
            style: {
                fill: '#e13b2e',
                stroke: '#e6fffb',
            },
            img: d.icon,
        }
        return true
    })
    G6.registerNode(
        'icon-node',
        {
            options: {
                stroke: '#e13b2e',
                fill: '#e13b2e',
            },
            draw(cfg: any, group: any) {
                const styles = this.getShapeStyle(cfg)
                const { labelCfg = {} } = cfg
                const w = styles.width
                const h = styles.height
                const keyShape = group.addShape('rect', {
                    attrs: {
                        ...styles,
                        x: -w / 2,
                        y: -h / 2 + 30,
                    },
                })
                if (cfg.leftIcon) {
                    // 设备类型图标
                    const { img } = cfg.leftIcon
                    group.addShape('image', {
                        attrs: {
                            x: 40 - w / 2,
                            y: -h / 2 + 2,
                            width: 40,
                            height: 33,
                            img,
                        },
                        name: 'device-type-image',
                    })
                }
                // 设备名称背景
                group.addShape('image', {
                    attrs: {
                        x: -handleTransfer(cfg.label) / 2,
                        y: -h / 2 + 36,
                        width: handleTransfer(cfg.label),
                        height: 24,
                        img: textBg,
                    },
                    name: 'device-name-image',
                })
                // 设备名称
                group.addShape('text', {
                    attrs: {
                        ...labelCfg.style,
                        text: cfg.label,
                        x: -w / 2 + 60,
                        y: -h / 2 + 36 + 20,
                        textAlign: 'center',
                    },
                    name: 'device-name-text',
                })
                if (cfg.hasCount) {
                    group.addShape('text', {
                        attrs: {
                            ...labelCfg.style,
                            fill: '#fff',
                            fontSize: 12,
                            text: cfg.count,
                            x: -w / 2 + 60,
                            y: -h / 2 + 10,
                            textAlign: 'center',
                        },
                        name: 'device-count',
                    })
                }
                // 当有子元素时才添加展开/收起图标
                if (cfg.children?.length) {
                    // 添加展开/收起图标
                    const expandOrCollapseIcon = group.addShape('marker', {
                        attrs: {
                            x: -w / 2 + 60,
                            y: -h / 2 + 76,
                            r: 6,
                            stroke: '#1e8ceb',
                            cursor: 'pointer',
                            symbol: cfg.collapsed ? EXPAND_ICON : COLLAPSE_ICON,
                        },
                        name: 'expand-collapse-icon',
                    })
                    // 图标被点击时切换图标
                    expandOrCollapseIcon.on('click', (e: any) => {
                        e.target.attrs.symbol = cfg.collapsed
                            ? COLLAPSE_ICON
                            : EXPAND_ICON
                    })
                }
                return keyShape
            },
            update: undefined,
        },
        'rect'
    )
    G6.registerEdge('flow-line', {
        // 绘制图形之间的连接线，即边
        draw(cfg: any, group: any) {
            const startPoint = cfg.startPoint
            const endPoint = cfg.endPoint
            const { style, targetNode } = cfg
            const targetModel = targetNode.getModel()
            const shiftingNum = targetModel.orderIndex * 5
            const centerLine = targetModel.hasTwoChild ? 0 : 1
            // debugger
            const flag = shiftingNum < 0 ? 1 : -1
            const gapHeight = defaultLayout.getVGap() / 2 - 18
            const shape = group.addShape('path', {
                attrs: {
                    stroke: style.stroke,
                    endArrow: style.endArrow,
                    path: [
                        [
                            'M',
                            startPoint.x + shiftingNum * centerLine,
                            startPoint.y + 30,
                        ],
                        [
                            'L',
                            startPoint.x + shiftingNum * centerLine,
                            (startPoint.y + endPoint.y) / 2 +
                                flag * shiftingNum,
                        ],
                        [
                            'L',
                            endPoint.x,
                            (startPoint.y + endPoint.y) / 2 +
                                flag * shiftingNum,
                        ],
                        [
                            'L',
                            endPoint.x,
                            (startPoint.y + endPoint.y) / 2 + gapHeight,
                        ],
                    ],
                },
            })
            return shape
        },
    })
}
const initChart = () => {
    const container: any = document.getElementById('container')
    const width = container.scrollWidth
    const height = container.scrollHeight || 500
    const minimap = new G6.Minimap({
        size: [150, 100],
    })
    const tooltip = new G6.Tooltip({
        getContent(evt: any) {
            const model = evt.item.getModel()
            const outDiv = document.createElement('div')
            outDiv.style.width = 'fit-content'
            outDiv.innerHTML = `<div>
            <div><span style="color: #1d72d4;font-weight: 600;font-size: 14px">名称:</span> <span>${
                model.label
            }</span> </div>
            <div style="margin:10px 0"><span style="color: #1d72d4;font-weight: 600;font-size: 14px">数量:</span> <span>${
                model.count || '--'
            }</span> </div>
            <div><span style="color: #1d72d4;font-weight: 600;font-size: 14px">层级:</span> <span>${
                model.depth
            }</span> </div>
            </div>`
            return outDiv
        },
        shouldBegin(evt: any) {
            const name = evt.target.get('name')
            const type = evt.target.get('type')
            if (name === 'device-name-text') {
                // 当鼠标悬浮到文本(即label)上时才展示tooltip
                return true
            }
            return false
        },
    })
    graph = new G6.TreeGraph({
        container: 'container',
        width,
        height,
        linkCenter: true,
        // plugins: [minimap],
        plugins: [tooltip],
        modes: {
            default: [
                'drag-canvas',
                'zoom-canvas',
                {
                    type: 'collapse-expand',
                    trigger: 'click',
                    shouldBegin: (e: any) => {
                        const name = e.target.get('name')
                        if (name === 'expand-collapse-icon') {
                            return true
                        }
                        return false
                    },
                },
            ],
        },
        defaultNode: {
            type: 'icon-node',
            size: [120, 40], // 整体item节点大小
            labelCfg: defaultLabelCfg,
        },
        defaultEdge: {
            type: 'flow-line',
            style: defaultEdgeStyle,
        },
        nodeStateStyles: defaultStateStyles,
        edgeStateStyles: defaultStateStyles,
        layout: defaultLayout,
    })
    graph.data(originalData)
    graph.render()
    graph.fitView()
    graph.on('itemcollapsed', () => {
        setTimeout(() => {
            graph.fitView()
        }, 600)
    })
    graph.on('node:click', (evt: any) => {
        const { item, target } = evt
        const targetType = target.get('type')
        const name = target.get('name')
        console.log(name)
        if (name === 'device-type-image') {
            alert('点我干啥子')
        }
    })
    if (typeof window !== 'undefined')
        window.onresize = () => {
            if (!graph || graph.get('destroyed')) return
            if (!container || !container.scrollWidth || !container.scrollHeight)
                return
            graph.changeSize(container.scrollWidth, container.scrollHeight)
        }
}
</script>

<template>
    <div class="chart-wrapper">
        <div id="container"></div>
    </div>
</template>

<style scoped lang="less">
.chart-wrapper {
    width: 100%;
    height: 100%;
    background-color: #254260e6 !important;
    // background-color: #11171d !important;
    #container {
        width: 100%;
        height: 100%;
    }
}
</style>
